package com.lh.service;

import com.lh.bean.Candidate;
import com.lh.bean.Voter;
import com.lh.config.CandidateCacheRepository;
import com.lh.exception.MyException;
import com.lh.mapper.CandidatesMapper;
import com.lh.mapper.VoterMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Service
public class VoteServiceImpl implements VoteService {

    private static final Logger logger = LoggerFactory.getLogger(VoteServiceImpl.class);

    @Autowired
    private VoterMapper voterMapper;
    @Autowired
    private CandidatesMapper candidatesMapper;
    @Value("${vote.limit.daily}")
    private int dailyVoteLimit;  // 每日投票次数限制
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private CandidateCacheRepository candidateCacheRepository;

    // 初始化时加载所有候选人数据并缓存到 Redis
    @PostConstruct
    public void initCandidatesCache() throws MyException {
        try {
            List<Candidate> candidates = candidatesMapper.getCandidates();
            for (Candidate candidate : candidates) {
                candidateCacheRepository.saveCandidate(candidate);
            }
            logger.info("Candidate cache initialized successfully.");
        } catch (Exception e) {
            logger.error("Failed to initialize candidate cache.", e);
            throw new MyException("初始化候选人缓存失败");
        }
    }

    // 投票
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer insertVote(String voterJwcode, String candidateJwcode, String voterName) throws MyException {
        Candidate candidate = candidateCacheRepository.getCandidate(candidateJwcode);
        System.out.println(candidate);
        if (candidateCacheRepository.getCandidate(candidateJwcode) == null) {
            throw new MyException("候选人不存在");
        }

        String redisKey = "vote_count:" + voterJwcode + ":" + LocalDateTime.now().toLocalDate();
        String currentVoteCountStr = stringRedisTemplate.opsForValue().get(redisKey);
        int voteCountToday = currentVoteCountStr == null ? 0 : Integer.parseInt(currentVoteCountStr);

        if (voteCountToday >= dailyVoteLimit) {
            throw new MyException("今日投票次数已达上限");
        }

        String voteStatusKey = "vote_status:" + voterJwcode + ":" + candidateJwcode;
        Boolean hasVoted = stringRedisTemplate.hasKey(voteStatusKey);
        if (hasVoted != null && hasVoted) {
            throw new MyException("您已经为该候选人投票，不能重复投票");
        }

        String candidateKey = "candidate:" + candidateJwcode;

        // 使用 Redis 的 Hash 增加候选人投票数
        redisTemplate.opsForHash().increment(candidateKey, "votes", 1);

        // 使用 Redis 的 ZSet 增加候选人投票数
        redisTemplate.opsForZSet().incrementScore("candidate:votes", candidateJwcode, 1);

        // 更新 Redis 中的投票次数
        redisTemplate.opsForValue().increment(redisKey, 1);

        //  设置 Redis 键的过期时间为当天的23:59:59
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime endOfDay = now.toLocalDate().atTime(23, 59, 59);
        long secondsUntilEndOfDay = Duration.between(now, endOfDay).getSeconds();
        //  更新投票次数的过期时间
        redisTemplate.expire(redisKey, secondsUntilEndOfDay, TimeUnit.SECONDS);
        //  更新投票重复键
        stringRedisTemplate.opsForValue().set(voteStatusKey, "true", secondsUntilEndOfDay, TimeUnit.SECONDS);

        // 数据库操作
        processVote(voterJwcode, candidateJwcode, voterName);

        return dailyVoteLimit - voteCountToday - 1;
    }

    // 获取所有候选人
    @Override
    public List<Candidate> getCandidates(String voterJwcode) {
        List<Candidate> candidateList = new ArrayList<>();
        try {
            Set<Object> jwCodes = candidateCacheRepository.getCandidateJwCodesByVotes();
            List<String> jwCodeList = new ArrayList<>();
            for (Object jwCode : jwCodes) {
                jwCodeList.add(jwCode.toString());
            }

            for (String jwCode : jwCodeList) {
                Candidate candidate = candidateCacheRepository.getCandidate(jwCode);
                if (candidate != null) {
                    candidateList.add(candidate);
                }
            }
        }catch(Exception e){
            //如果缓存中获取候选人失败，则从数据库中获取候选人列表
            candidateList = candidatesMapper.getCandidates();
        }

        // 插入投票记录，为 List 插入当日是否投过票的状态
        markVotedStatus(voterJwcode, candidateList);

        return candidateList;
    }

    // 获取候选人被投票记录
    @Override
    public List<Voter> getVotesByCandidate(String candidateJwcode) throws MyException {
        try {
            return voterMapper.getVotesByCandidate(candidateJwcode);
        } catch (Exception e) {
            throw new MyException("获取候选人投票记录失败");
        }
    }

    private void markVotedStatus(String voterJwcode, List<Candidate> candidateList) {
        String voteStatusKeyPrefix = "vote_status:" + voterJwcode + ":";
        Set<String> votedKeys = stringRedisTemplate.keys(voteStatusKeyPrefix + "*");

        if (votedKeys != null) {
            for (Candidate candidate : candidateList) {
                String fullKey = voteStatusKeyPrefix + candidate.getJwCode();
                if (votedKeys.contains(fullKey)) {
                    candidate.setVoted(true);
                }
            }
        }
    }

    @Async
    @Transactional(rollbackFor = Exception.class)
    protected void processVote(String voterJwcode, String candidateJwcode, String voterName) throws MyException {
        // 获取候选人信息
        Candidate candidate = candidatesMapper.getByCandidateJwcode(candidateJwcode);

        // 2. 获取候选人信息
        if (candidate == null) {
            throw new MyException("候选人不存在！");
        }

        // 4. 增加候选人票数
        if (!candidatesMapper.addVotes(candidateJwcode)) {
            throw new MyException ("候选人票数更新失败，请联系管理员！");
        }

        // 5. 插入投票记录
        voterMapper.insertVote(voterJwcode, candidateJwcode, voterName);


        System.out.println("投票成功！用户：" + voterJwcode + " 投给了 " + candidateJwcode);
    }

    //重置投票
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean resetVote() throws MyException {
        if (voterMapper.resetVote() && candidatesMapper.resetVotes()){
            //删除redis所有缓存
            redisTemplate.delete(redisTemplate.keys("*"));
            candidateCacheRepository.deleteAllCandidatesFromCache();
        } else {
            throw new MyException("重置投票失败");
        }
        //重新初始化
        initCandidatesCache();
        return true;
    }

}
